package com.github.cloudgyb.proxy.server;

import com.github.cloudgyb.proxy.ProxyApplication;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.http.HttpServerCodec;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 代理服务器实现
 *
 * @author cloudgyb
 */
public class ProxyServer {
    private final static Logger logger = LoggerFactory.getLogger(ProxyApplication.class);
    public final static ProxyExceptionHandler proxyExceptionHandler = new ProxyExceptionHandler();
    private final NioEventLoopGroup boss = new NioEventLoopGroup();
    private final NioEventLoopGroup worker = new NioEventLoopGroup();
    private final String host;
    private final int port;

    private final ProxyServerAuthenticationConfig authenticationConfig;
    private final ProxyServerBasicAuthenticationHandler proxyServerBasicAuthenticationHandler;

    public ProxyServer(String host, int port) {
        this.host = host;
        this.port = port;
        this.authenticationConfig = new ProxyServerAuthenticationConfig(false, null, null);
        this.proxyServerBasicAuthenticationHandler = null;
    }

    public ProxyServer(String host, int port, boolean enableProxyAuth, String username, String password) {
        this.host = host;
        this.port = port;
        this.authenticationConfig = new ProxyServerAuthenticationConfig(enableProxyAuth, username, password);
        this.proxyServerBasicAuthenticationHandler = new ProxyServerBasicAuthenticationHandler(this.authenticationConfig);
    }

    public void start() {
        ServerBootstrap serverBootstrap = new ServerBootstrap();

        ChannelFuture future = serverBootstrap
                .channel(NioServerSocketChannel.class)
                .group(boss, worker)
                .option(ChannelOption.SO_BACKLOG, 128)
                .childHandler(new ChannelInitializer<NioSocketChannel>() {
                    @Override
                    protected void initChannel(NioSocketChannel channel) {
                        ChannelPipeline pipeline = channel.pipeline();

                        pipeline.addLast("httpServerCodec", new HttpServerCodec());

                        if (authenticationConfig.enableProxyAuth()) {
                            pipeline.addLast(proxyServerBasicAuthenticationHandler);
                        }

                        pipeline.addLast("proxyServer", new ProxyServerHandler())
                                .addLast("exceptionHandler", proxyExceptionHandler)
                                .addLast("idleHandler",
                                        new IdleHandler(10, 10, 20));

                    }
                }).bind(host, port);

        future.addListener(future1 -> {
            if (future1.isSuccess()) {
                logger.info("Proxy Server listen at {}:{}...", host, port);
                future.channel().closeFuture().addListener(future2 -> stop());
            } else {
                stop();
            }
        });
    }

    /**
     * 停止代理服务器
     */
    public void stop() {
        worker.shutdownGracefully();
        boss.shutdownGracefully();
        logger.info("Proxy Server stopping...");
    }
}
